#pragma once

#include "protocol.hpp"
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cassert>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <pthread.h>

using namespace std;

enum
{
    USAGE_ERR = 0,
    SOCKET_ERR,
    BIND_ERR,
    LISTEN_ERR,
};

const string defaultIp = "0.0.0.0";
const int gbacklog = 5;

struct ThreadData
{
    int sockfd;
    struct sockaddr_in client;
    func_t func;
};

void serviceIO(int sockfd, const struct sockaddr_in& client, func_t func)
{
    string inbuffer;
    string clientip = inet_ntoa(client.sin_addr);
    uint16_t clientport = ntohs(client.sin_port);
    while (true)
    {
        string req_str, req_content;
        if (!recvPackage(sockfd, inbuffer, &req_str))//从客户端接收一个完整的请求报文
        {
            //客户端已退出 无法继续获取报文
            cout << "[" << clientip << "]" << "[" << clientport << "]" << "client quit me too" << endl;
            return;
        }
        if (!deLength(req_str, &req_content))//请求去报头
            return;
        cout << "[" << clientip << "]" << "[" << clientport << "]" << req_content << endl;
        Request req;
        if (!req.deserialize(req_content))//请求反序列化
            return;

        Response res;
        func(req, &res);//业务逻辑处理 得到请求处理结果

        string res_str, res_content;
        res.serialize(&res_content);//处理结果序列化
        enLength(res_content, &res_str);//处理结果加报头
        send(sockfd, res_str.c_str(), res_str.size(), 0);
    }
}

class calServer
{
public:
    calServer(uint16_t port, const string& ip = defaultIp)
    : _listenfd(-1),
      _ip(ip),
      _port(port)
    {}

    void initServer()
    {
        //signal(SIGCHLD, SIG_IGN);//忽略进程等待信号 父进程无需等待回收子进程
        //创建socket
        _listenfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_listenfd == -1)
            exit(SOCKET_ERR);
        cout << "create socket success, listenfd: " << _listenfd << endl;
        //绑定ip和port
        struct sockaddr_in server;
        memset(&server, 0, sizeof server);
        server.sin_family = AF_INET;
        server.sin_addr.s_addr = INADDR_ANY;//绑定任意ip 使客户端可以通过多个ip向服务器通信
        server.sin_port = htons(_port);
        if (bind(_listenfd, (struct sockaddr*)&server, sizeof server) == -1)
            exit(BIND_ERR);
        cout << "bind success" << endl;
        //设置监听
        if (listen(_listenfd, gbacklog) != 0)
            exit(LISTEN_ERR);
        cout << "listen success" << endl;
    }

    void startServer(func_t func)
    {
        while (true)
        {
            //阻塞式接收客户端的链接
            struct sockaddr_in client;
            socklen_t len;
            int sockfd = accept(_listenfd, (struct sockaddr*)&client, &len);
            if (sockfd == -1)
                continue;
            cout << "accept success, sockfd: " << sockfd << endl;
            string clientIp(inet_ntoa(client.sin_addr));
            ThreadData* td = new ThreadData;
            td->client = client;
            td->sockfd = sockfd;
            td->func = func;
            //多进程版
            //孙子进程
            // pid_t id = fork();
            // if (id == 0)
            // {
            //     close(_listenfd);//文件引用计数-1 不会释放内核struct file结构体
            //     if (fork() > 0)
            //         exit(-1);//子进程退出
            //     //到这里为孙子进程 由于子进程已退出 孙子进程变为孤儿进程 后续由os回收
            //     serviceIO(sockfd, clientIp);//孙子进程处理客户端数据并向客户端返回处理结果
            //     close(sockfd);
            //     exit(-1);//客户端退出后 孙子进程也需要退出
            // }
            // //到这里为父进程
            // close(sockfd);
            // //父进程继续循环 接收其他客户端的链接

            //信号忽略
            // if (fork() > 0)
            // {
            //     close(_listenfd);
            //     serviceIO(sockfd, clientIp);
            //     close(sockfd);
            //     exit(-1);
            // }
            // close(sockfd);
            /*
            多进程版本下 父进程会释放sockfd 但实际是减少引用计数 struct file由子进程释放
            所以视觉上父进程accept创建的是相同的文件描述符 但实际上指向的struct file不同
            */

            pthread_t tid;
            pthread_create(&tid, nullptr, start_routine, td);
            /*
            多线程版本下 sockfd由从线程处理完客户端业务后释放 同时释放struct file 主线程无需关注文件描述符泄露的问题
            */
        }
    }

    static void *start_routine(void *args)
    {
        pthread_detach(pthread_self());//与主线程断开连接 主线程无需等待回收从线程
        ThreadData* td = (ThreadData*)args;
        serviceIO(td->sockfd, td->client, td->func);
        close(td->sockfd);//主从线程共享文件描述符表 从线程会释放struct file文件结构体
        delete td;
        return nullptr;
    }

private:
    int _listenfd;
    string _ip;
    uint16_t _port;
};